﻿using System;
using System.Collections.Generic;

namespace cludg.Utils.Html
{
	using Document = HtmlAgilityPack.HtmlDocument;
	using Node = HtmlAgilityPack.HtmlNode;

	public static class Sanitizer
	{
		private static readonly string[] AllowedElements = "p|br|h1|h2|h3|h4|h5|h6|b|i|div|span|#text|strong|em|ol|ul|li|blockquote|pre|code|a".Split('|');

		public static string Sanitize(string html)
		{
			var doc = new Document();
			doc.LoadHtml(html);

			var unwanted = new List<Node>();

			foreach (var childNode in doc.DocumentNode.ChildNodes)
			{
				FindUnwantedNodes(childNode, unwanted);
			}

			foreach (var htmlNode in unwanted)
			{
				if (doc.DocumentNode.ChildNodes.Contains(htmlNode))
					doc.DocumentNode.RemoveChild(htmlNode);
			}

			var nodes = doc.DocumentNode.SelectNodes("//a[starts-with(@href, 'javascript')]|//a[starts-with(@href, 'jscript')]|//a[starts-with(@href, 'vbscript')]");

			if (nodes != null)
			{
				foreach (var node in nodes)
				{
					node.SetAttributeValue("href", "protected");
				}
			}

			nodes = doc.DocumentNode.SelectNodes("//a");

			if (nodes != null)
			{
				foreach (var node in nodes)
				{
					node.SetAttributeValue("rel", "nofollow");
				}
			}

			nodes = doc.DocumentNode.SelectNodes("//*[@onclick or @onmouseover or @onfocus or @onblur or @onmouseout or @ondoubleclick or @onload or @onunload or @style or @class or @id or @title]");

			if (nodes != null)
			{
				foreach (var node in nodes)
				{
					node.Attributes.Remove("onclick");
					node.Attributes.Remove("onmouseover");
					node.Attributes.Remove("onfocus");
					node.Attributes.Remove("onblur");
					node.Attributes.Remove("onmouseout");
					node.Attributes.Remove("ondoubleclick");
					node.Attributes.Remove("onload");
					node.Attributes.Remove("onunload");

					node.Attributes.Remove("style");
					node.Attributes.Remove("class");
					node.Attributes.Remove("id");
					node.Attributes.Remove("title");
				}
			}

			return doc.DocumentNode.InnerHtml;
		}

		private static void FindUnwantedNodes(Node node, List<Node> unwanted)
		{
			if (Array.IndexOf(AllowedElements, node.Name) > -1)
			{
				foreach (Node childNode in node.ChildNodes)
				{
					FindUnwantedNodes(childNode, unwanted);
				}
			}
			else
			{
				unwanted.Add(node);
			}
		}
	}
}
